package at.donalfonso.loadbalancer;

import java.util.ArrayList;

import org.mozartspaces.capi3.CoordinationData;
import org.mozartspaces.capi3.KeyCoordinator;
import org.mozartspaces.capi3.LindaCoordinator;
import org.mozartspaces.capi3.Selector;
import org.mozartspaces.core.Capi;
import org.mozartspaces.core.Entry;
import org.mozartspaces.core.MzsConstants.RequestTimeout;
import org.mozartspaces.core.MzsCoreException;
import org.mozartspaces.core.TransactionReference;

import at.donalfonso.model.LieferBestellung;
import at.donalfonso.model.LieferBestellungStatus;
import at.donalfonso.utility.SpaceUtility;

public class LoadBalancer {
	private String id;
	private SpaceUtility fil1, fil2;
	private Capi capi1, capi2;
	
	public LoadBalancer(String id) {
		this.id = id;
		fil1 = new SpaceUtility(1);
		fil2 = new SpaceUtility(2);
		capi1 = fil1.getCapi();
		capi2 = fil2.getCapi();
		System.out.println("LoadBalancer: " + id);
	}
	
	public static void main(String args[]) {
		LoadBalancer lb; 

		if (args.length < 1) {
			System.out.println("Usage: java LoadBalancer <ID>");
			lb = new LoadBalancer("LB1");
		} 
		else {
			lb = new LoadBalancer(args[0]);
		}
		
		try {
			while (true) {
				lb.loadbalance();
				Thread.sleep(100);
			}
		} catch(MzsCoreException | InterruptedException | NullPointerException e) {
			e.printStackTrace();
		}
	}
	
	public void loadbalance() throws MzsCoreException, NullPointerException {
		LieferBestellung template = new LieferBestellung();
		template.setLieferstatus(LieferBestellungStatus.BESTELLT);
		Selector sel = LindaCoordinator.newSelector(template, Selector.COUNT_MAX);
		CoordinationData coord = LindaCoordinator.newCoordinationData();
		int offenelb1 = capi1.test(fil1.getBestellungOffenContainer(), sel, RequestTimeout.TRY_ONCE, null);
		int offenelb2 = capi2.test(fil2.getBestellungOffenContainer(), sel, RequestTimeout.TRY_ONCE, null);
		int avg = (offenelb1 + offenelb2) / 2;
		TransactionReference tx1 = capi1.createTransaction(1000, fil1.getUri()); 
		TransactionReference tx2 = capi2.createTransaction(1000, fil2.getUri()); 
		ArrayList<LieferBestellung> llb;
		ArrayList<Entry> le_neuefil = new ArrayList<Entry>();
		ArrayList<Entry> le_altefil = new ArrayList<Entry>();
		
		if ((offenelb1 + 1) < offenelb2) {
			sel = LindaCoordinator.newSelector(template, offenelb2 - avg);
			llb = capi2.take(fil2.getBestellungOffenContainer(), sel, RequestTimeout.TRY_ONCE, tx2);
			for (LieferBestellung lb: llb) {
				lb.setLieferstatus(LieferBestellungStatus.BESTELLT);
				lb.setLoadBalancer(id);
				le_neuefil.add(new Entry(lb, coord));
				LieferBestellung lbn = new LieferBestellung(lb);
				lbn.setLieferstatus(LieferBestellungStatus.VERSCHOBEN);
				lb.setLoadBalancer(id);
				le_altefil.add(new Entry(lbn));
			}
			capi1.write(le_neuefil, fil1.getBestellungOffenContainer(), RequestTimeout.TRY_ONCE, tx1);
			capi2.write(le_altefil, fil2.getSaalContainer(), RequestTimeout.TRY_ONCE, tx2);
			System.out.println((offenelb2-avg) + " Lieferbestellungen erfolgreich von Fil2 zu Fil1 verschoben.");
		}
		else if ((offenelb1 - 1) > offenelb2) {
			sel = LindaCoordinator.newSelector(template, offenelb1 - avg);
			llb = capi1.take(fil1.getBestellungOffenContainer(), sel, RequestTimeout.TRY_ONCE, tx1);
			for (LieferBestellung lb: llb) {
				lb.setLieferstatus(LieferBestellungStatus.BESTELLT);
				lb.setLoadBalancer(id);		
				le_neuefil.add(new Entry(lb, coord));
				LieferBestellung lbn = new LieferBestellung(lb);
				lbn.setLieferstatus(LieferBestellungStatus.VERSCHOBEN);
				lbn.setLoadBalancer(id);	
				le_altefil.add(new Entry(lbn));
			}
			capi2.write(le_neuefil, fil2.getBestellungOffenContainer(), RequestTimeout.TRY_ONCE, tx2);
			capi1.write(le_altefil, fil1.getSaalContainer(), RequestTimeout.TRY_ONCE, tx1);
			System.out.println((offenelb1-avg) + " Lieferbestellungen erfolgreich von Fil1 zu Fil2 verschoben.");
		}
		// else nothing to do
		
		capi1.commitTransaction(tx1);
		capi2.commitTransaction(tx2);
	}
}
